﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;


using System.Threading;
using System.Collections.Concurrent;
using BluSteamServer.Model.DataAccessLayer;

namespace BluSteamServer.Model
{
    public class ConnectionManager
    {
        #region Fields

        private static ConnectionManager _instance;

        private Dictionary<string, Socket> _connections;
        private List<string> _connectedUsernames;
        private List<string> _registeredUsernames;

        public event EventHandler UserConnect;
        public event EventHandler UserDisconnect;

        private Object _valueLock = new Object();

        #endregion

        #region Constructors

        private ConnectionManager()
        {
            _connections = new Dictionary<string, Socket>();
            _connectedUsernames = new List<string>();
            _registeredUsernames = new List<string>();
        }

        #endregion

        #region Properties

        public static ConnectionManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ConnectionManager();
                }
                return _instance;
            }
        }

        public List<string> ConnectedUsernames
        {
            get
            {
                if (_connections != null)
                {
                    return new List<string>(_connections.Keys);
                }
                return null;
            }
        }

        public List<string> RegisteredUsernames
        {
            get
            {
                List<string> usernamesList = new List<string>();
                DataAccessObject dao = new DataAccessObject();
                List<Dictionary<string, object>> users = dao.GetAllFromTable("Users");

                foreach (Dictionary<string, object> user in users)
                {
                    usernamesList.Add(user["username"] as string);
                }
             
                return usernamesList;
            }
        }

        public object ValueLock
        {
            get { return _valueLock; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds an new connection to the connection dictionary.
        /// </summary>
        public void AddConnection(string username, Socket socket)
        {
            Monitor.Enter(_valueLock);

            if (!_connections.ContainsKey(username))
            {
                _connections.Add(username, socket);
            }
            else
            {
                // Log error message
                Debug.WriteLine("Error: Can not add connection because key already exists.");
            }

            if (UserConnect != null)
            {
                UserConnect(this, EventArgs.Empty);
            }

            if (socket != null)
            {
                Debug.WriteLine("Connected Client: " + username);
                Debug.WriteLine("Client IP: {0}", socket.RemoteEndPoint);
            }

            Monitor.Exit(_valueLock);
        }

        /// <summary>
        /// Returns a connected TCP Socket based on the username supplied by the calling routine.
        /// </summary>
        public Socket GetSocket(string username)
        {
            Monitor.Enter(_valueLock);

            Socket socket = null;
            if (_connections.ContainsKey(username))
            {
                socket = _connections[username];
                Monitor.Exit(_valueLock);
                return socket;
            }

            Monitor.Exit(_valueLock);
            return null;
        }

        /// <summary>
        /// Checks if a client is connected to the server based on the username supplied by the calling routine.
        /// </summary>
        public bool IsConnected(string username)
        {
            Monitor.Enter(_valueLock);
            bool connected = _connections.ContainsKey(username);
            Monitor.Exit(_valueLock);

            return _connections.ContainsKey(username);
        }

        /// <summary>
        /// Returns a dictionary containing connected clients usernames and TCP Sockets.
        /// </summary>
        public Dictionary<string, Socket> GetConnectionDictionary()
        {
            Monitor.Enter(_valueLock);
            Dictionary<string, Socket> connections = _connections;
            Monitor.Exit(_valueLock);

            return _connections;
        }

        public void RemoveConnection(string username)
        {
            Monitor.Enter(_valueLock);

            if (_connections.ContainsKey(username))
            {   
                // close socket first
                //Socket socket = this.GetSocket(username);
                //if (socket != null)
                //{
                //    socket.Close();
                //}
                _connections.Remove(username);
            }
            else
                Debug.WriteLine("Error: Can not remove connection because key was not matched.");

            if (UserDisconnect != null)
            {
                UserDisconnect(this, EventArgs.Empty);
            }

            Monitor.Exit(_valueLock);
        }

        #endregion
    }
}
